Skip to content

Fix #2458: send body when no 100 Continue arrives over TLS#2460

Merged
yhirose merged 1 commit into
masterfrom
fix-2458-expect-100-continue-tls
May 29, 2026
Merged

Fix #2458: send body when no 100 Continue arrives over TLS#2460
yhirose merged 1 commit into
masterfrom
fix-2458-expect-100-continue-tls

Conversation

@yhirose
Copy link
Copy Markdown
Owner

@yhirose yhirose commented May 29, 2026

Problem

Client::Put/Post with a body of at least CPPHTTPLIB_EXPECT_100_THRESHOLD (1024) bytes auto-adds Expect: 100-continue. Over TLS, this could fail with Failed to read connection (#2458): the server received the request but the client never sent the body.

Root cause

The 100-continue wait decided whether to withhold the body based on raw socket readability (select_read on the underlying socket). Over TLS, post-handshake records such as TLS 1.3 session tickets make the socket readable without any HTTP response being available. The client mistook that for an incoming response, withheld the body, and then blocked reading a response that never came (the server was waiting for the body), eventually timing out as Read.

This matches the report: it only happened with large bodies (Expect triggered), only over HTTPS, and disappeared with CPPHTTPLIB_EXPECT_100_THRESHOLD 0. curl succeeds against the same server because it sends the body once no 100 Continue arrives.

Fix

Base the decision on whether a status line can actually be read within the 100-continue timeout, not on raw socket readability:

  • temporarily shorten the read timeout to the 100-continue timeout,
  • try to read the status line,
  • if none arrives, send the body and proceed as usual (matching curl).

The 100 Continue path and the early final-response path (e.g. 417/414) keep working, and the client is no longer fooled by TLS records.

Test

Adds Expect100ContinueTest.TLSServerOmits100Continue: a raw OpenSSL server (TLS 1.3) that never sends 100 Continue. It reproduces the hang on the previous code (fails with Failed to read connection) and passes with the fix.

  • Full suite: 694/694 passing (OpenSSL, ASAN).
  • test_split build verified.
  • New test is guarded to OpenSSL/non-Windows; the fix itself is backend-agnostic.

🤖 Generated with Claude Code

The auto-added `Expect: 100-continue` (for bodies >= 1024 bytes) decided
whether to withhold the request body based on raw socket readability via
select_read(). Over TLS, post-handshake records such as TLS 1.3 session
tickets make the socket readable without any HTTP response being
available, so the client withheld the body and then blocked reading a
response that never came, failing with `Failed to read connection`.

Decide based on whether a status line can actually be read within the
100-continue timeout instead: temporarily shorten the read timeout, try
to read the status line, and if none arrives, send the body and proceed
as usual (matching curl). This keeps the `100 Continue` and early
final-response paths working while no longer being fooled by TLS records.

Add a regression test using a raw OpenSSL server that never sends
`100 Continue`.
@yhirose yhirose merged commit 91219d4 into master May 29, 2026
43 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant